package dts_tcp_client

import (
	"context"
	"fmt"
	"github.com/sirupsen/logrus"
	"net"
	"sync"
	"time"
)

const (
	FlagTemperature DataFlag = 0x00
	FlagAntiStokes  DataFlag = 0x01
	FlagStokes      DataFlag = 0x02

	DataFrameTemperatureStart                  = 0xaa
	DataFrameFiberBrokenAlarmStart             = 0xbc // 断纤报警
	DataFrameConstantTemperatureAlarmStart     = 0xa6 // 高温定温报警
	DataFrameDifferentialTemperatureAlarmStart = 0xa5 // 高温差温报警
	DataFrameYesStart                          = 0xaf
	DataFrameNoStart                           = 0xa0

	DataFrameEnd = 0x55

	HeaderLength = 11 // 包头长度,包括当前包的总长度在内

	F = logrus.FatalLevel
	E = logrus.ErrorLevel
	W = logrus.WarnLevel
	I = logrus.InfoLevel
	D = logrus.DebugLevel

	NotConnected Status = 1 // 未连接
	Connecting   Status = 2 // 正在连接
	Connected    Status = 3 // 已连接
	Disconnected Status = 4 // 连接断开

	Broken                  AlarmType = 1 // 断纤
	ConstantTemperature     AlarmType = 2 // 常温报警
	DifferentialTemperature AlarmType = 3 // 温差报警
)

type Status byte

func (s *Status) toInt32() int32 {
	return int32(*s)
}

func (s *Status) toPointInt32() *int32 {
	i := int32(*s)
	return &i
}

func (s *Status) IsConnected() bool {
	return *s == Connected
}

func (s *Status) String() string {
	switch *s {
	case NotConnected:
		return "未连接"
	case Connecting:
		return "正在连接"
	case Connected:
		return "已连接"
	case Disconnected:
		return "连接断开"
	}
	return "未知状态"
}

type Config struct {
	Id             int           // 设备id
	Name           string        // 设备名称
	Server         string        // dts服务器地址
	Debug          bool          // 调试模式
	Precision      float32       // 数据精度
	ChannelMapping map[byte]int  // 通道号与通道id对应关系
	FilterSignal   bool          // 是否过滤信号数据
	Reconnect      bool          // 是否重连
	Timeout        time.Duration // 连接超时时间
}

var ChannelMapping = func(ids ...int) map[byte]int {
	mapping := map[byte]int{}
	for i, id := range ids {
		mapping[byte(i+1)] = id
	}
	return mapping
}

type Message struct {
	Level   string
	Message string
	At      time.Time
}

type DataFlag byte

type AlarmType byte

type CallChannelTemperature func(ChannelTemperature)

type CallFiberBroken func(FiberBroken)

type CallTemperatureAlarm func(TemperatureAlarm)

type CallMessage func(Message)

type CallConnectChange func(Status)

type Client struct {
	ctx                    context.Context
	cancel                 context.CancelFunc
	parentContext          context.Context
	wait                   sync.WaitGroup
	config                 *Config // 配置
	info                   Info
	addr                   *net.TCPAddr
	conn                   *net.TCPConn // 连接实例
	status                 int32
	locker                 sync.RWMutex
	callMessage            map[string]CallMessage
	callFiberBroken        map[string]CallFiberBroken
	callConnectChange      map[string]CallConnectChange
	callTemperatureAlarm   map[string]CallTemperatureAlarm
	callChannelTemperature map[string]CallChannelTemperature
}

func NewClient(ctx context.Context, config *Config) *Client {
	var (
		addr *net.TCPAddr
		err  error
	)

	if config.Precision == 0 {
		config.Precision = 0.5
	}

	addr, err = net.ResolveTCPAddr("tcp", config.Server)
	if err != nil {
		addr = &net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 5050}
	}

	if config.Id == 0 {
		config.Id = int(addr.IP.To4()[3])
	}

	if config.Name == "" {
		config.Name = fmt.Sprintf("DTS%d:%s", config.Id, config.Server)
	}

	if config.Timeout == 0 {
		config.Timeout = time.Second * 5
	}

	client := &Client{
		parentContext:          ctx,
		config:                 config,
		addr:                   addr,
		status:                 int32(NotConnected),
		info:                   Info{Id: config.Id, Name: config.Name},
		callChannelTemperature: map[string]CallChannelTemperature{},
		callFiberBroken:        map[string]CallFiberBroken{},
		callTemperatureAlarm:   map[string]CallTemperatureAlarm{},
		callMessage:            map[string]CallMessage{},
		callConnectChange:      map[string]CallConnectChange{},
	}
	return client
}

func (c *Client) CallChannelTemperature(t string, call CallChannelTemperature) {
	c.locker.Lock()
	defer c.locker.Unlock()
	c.callChannelTemperature[t] = call
}

func (c *Client) RemoveChannelTemperature(t string) {
	c.locker.Lock()
	defer c.locker.Unlock()
	delete(c.callChannelTemperature, t)
}

func (c *Client) CallFiberBroken(t string, call CallFiberBroken) {
	c.locker.Lock()
	defer c.locker.Unlock()
	c.callFiberBroken[t] = call
}

func (c *Client) RemoveCallFiberBroken(t string) {
	c.locker.Lock()
	defer c.locker.Unlock()
	delete(c.callFiberBroken, t)
}

func (c *Client) CallTemperatureAlarm(t string, call CallTemperatureAlarm) {
	c.locker.Lock()
	defer c.locker.Unlock()
	c.callTemperatureAlarm[t] = call
}

func (c *Client) RemoveTemperatureAlarm(t string) {
	c.locker.Lock()
	defer c.locker.Unlock()
	delete(c.callTemperatureAlarm, t)
}

func (c *Client) CallMessage(t string, call CallMessage) {
	c.locker.Lock()
	defer c.locker.Unlock()
	c.callMessage[t] = call
}

func (c *Client) RemoveCallMessage(t string) {
	c.locker.Lock()
	defer c.locker.Unlock()
	delete(c.callMessage, t)
}

func (c *Client) CallConnectChange(t string, call CallConnectChange) {
	c.locker.Lock()
	defer c.locker.Unlock()
	c.callConnectChange[t] = call
}

func (c *Client) RemoveCallConnectChange(t string) {
	c.locker.Lock()
	defer c.locker.Unlock()
	delete(c.callConnectChange, t)
}

type Info struct {
	Id           int    `json:"id"`
	Name         string `json:"name"`
	ChannelId    int    `json:"channel_id"`
	DeviceNumber byte   `json:"device_number"` // 设备号
}

// ChannelTemperature 通道温度
type ChannelTemperature struct {
	Info          `json:"info"`
	Flag          DataFlag  `json:"flag"`
	ChannelNumber byte      `json:"channel_number"` // 通道号
	ChannelCount  byte      `json:"channel_count"`  // 通道数
	Start         float32   `json:"start"`          // 开始位置
	End           float32   `json:"end"`            // 结束位置
	Temperatures  []float32 `json:"temperatures"`   // 温度数据
	At            time.Time `json:"at"`             //
}

// FiberBroken 断纤信息
type FiberBroken struct {
	Info          `json:"info"`
	ChannelNumber byte      `json:"channel_number"` // 通道号
	Position      float32   `json:"position"`       // 断纤位置
	At            time.Time `json:"at"`             //
}

// TemperatureAlarm 报警信息
type TemperatureAlarm struct {
	Info          `json:"info"`
	ChannelNumber byte      `json:"channel_number"` // 通道号
	Alarms        []Alarm   `json:"alarms,omitempty"`
	Type          AlarmType `json:"type"`
	At            time.Time `json:"at"` //
}

type Alarm struct {
	AreaNumber  int16   `json:"area_number"`
	Position    float32 `json:"position"`
	Temperature float32 `json:"temperature"`
}

func (a Alarm) String() string {
	return fmt.Sprintf("区域号: %d,报警位置: %.2fm, 报警温度: %.f℃", a.AreaNumber, a.Position, a.Temperature)
}

func (t *TemperatureAlarm) AlarmsString() string {
	str := ""
	for _, alarm := range t.Alarms {
		str += alarm.String() + "\n"
	}
	return str
}
